// il - A image library representation.

// Copyright (C) 2005-2006 VM Inc.
// Released under the LGPL.
// For more information, see http://www.openlibraries.org.

#include <algorithm>
#include <limits>

using namespace std;

#include "il.hpp"

//#include <openpluginlib/pl/utf8_utils.hpp>
namespace opl = olib::openpluginlib;

namespace olib { namespace openimagelib { namespace il {

// The following private functions are a bit rough and shouldn't be exposed publicly
// All access to the functions are via the public convert and allocate functions

typedef image< unsigned char, l8 >				l8_image_type;
typedef image< unsigned char, l8a8 >			l8a8_image_type;
typedef image< unsigned char, r8g8b8 >			r8g8b8_image_type;
typedef image< unsigned char, b8g8r8 >			b8g8r8_image_type;
typedef image< unsigned char, b8g8r8a8 >		b8g8r8a8_image_type;
typedef image< unsigned char, r8g8b8a8 >		r8g8b8a8_image_type;
typedef image< unsigned char, a8r8g8b8 >		a8r8g8b8_image_type;
typedef image< unsigned char, a8b8g8r8 >		a8b8g8r8_image_type;
typedef image< unsigned char, r16g16b16a16f >	r16g16b16a16f_image_type;
typedef image< unsigned char, yuv444 >			yuv444_image_type;
typedef image< unsigned char, yuv422 >			yuv422_image_type;
typedef image< unsigned char, yuv422p >			yuv422p_image_type;
typedef image< unsigned char, yuv420p >			yuv420p_image_type;
typedef image< unsigned char, yuv411 >			yuv411_image_type;
typedef image< unsigned char, yuv411p >			yuv411p_image_type;
typedef const unsigned char *					const_pointer;
typedef unsigned char *							pointer;
typedef int 									size_type;

// Public allocation method
IL_DECLSPEC image_type_ptr allocate( const std::wstring &pf, int width, int height )
{
	image_type_ptr dst_img = image_type_ptr( );

	if ( pf == L"r8g8b8" )
		dst_img = image_type_ptr( new image_type( r8g8b8_image_type( width, height, 1 ) ) );
	else if ( pf == L"b8g8r8" )
		dst_img = image_type_ptr( new image_type( b8g8r8_image_type( width, height, 1 ) ) );
	if ( pf == L"r8g8b8a8" )
		dst_img = image_type_ptr( new image_type( r8g8b8a8_image_type( width, height, 1 ) ) );
	else if ( pf == L"b8g8r8a8" )
		dst_img = image_type_ptr( new image_type( b8g8r8a8_image_type( width, height, 1 ) ) );
	else if( pf == L"a8r8g8b8" )
		dst_img = image_type_ptr( new image_type( a8r8g8b8_image_type( width, height, 1 ) ) );
	else if( pf == L"a8b8g8r8" )
		dst_img = image_type_ptr( new image_type( a8b8g8r8_image_type( width, height, 1 ) ) );
	else if( pf == L"r16g16b16a16f" )
		dst_img = image_type_ptr( new image_type( r16g16b16a16f_image_type( width, height, 1 ) ) );
	else if ( pf == L"yuv444" )
		dst_img = image_type_ptr( new image_type( yuv444_image_type( width, height, 1 ) ) );
	else if ( pf == L"yuv422" )
		dst_img = image_type_ptr( new image_type( yuv422_image_type( width, height, 1 ) ) );
	else if ( pf == L"yuv422p" )
		dst_img = image_type_ptr( new image_type( yuv422p_image_type( width, height, 1 ) ) );
	else if ( pf == L"yuv420p" )
		dst_img = image_type_ptr( new image_type( yuv420p_image_type( width, height, 1 ) ) );
	else if ( pf == L"yuv411" )
		dst_img = image_type_ptr( new image_type( yuv411_image_type( width, height, 1 ) ) );
	else if ( pf == L"yuv411p" )
		dst_img = image_type_ptr( new image_type( yuv411p_image_type( width, height, 1 ) ) );
	else if ( pf == L"l8" )
		dst_img = image_type_ptr( new image_type( l8_image_type( width, height, 1 ) ) );
	else if ( pf == L"l8a8" )
		dst_img = image_type_ptr( new image_type( l8a8_image_type( width, height, 1 ) ) );
	
	return dst_img;
}

// Convenience method to allocate an image of the output format, ensuring all the required
// information from the source is inherited
inline image_type_ptr allocate( const image_type_ptr &src_img, const std::wstring &format )
{
	image_type_ptr dst_img = il::allocate( format, src_img->width( ), src_img->height( ) );

	if ( dst_img )
	{
		dst_img->set_flipped( src_img->is_flipped( ) );
		dst_img->set_flopped( src_img->is_flopped( ) );
		dst_img->set_field_order( src_img->field_order( ) );
	}

	return dst_img;
}

// Obtain the fields in presentation order
IL_DECLSPEC image_type_ptr field( const image_type_ptr &im, int field )
{
	// Default our return to the source
	image_type_ptr new_im = im;

	// Only do something if we have an image and it isn't progressive
	if ( im && im->field_order( ) != progressive )
	{
		// Allocate an image which is half the size of the source
		new_im = allocate( im->pf( ), im->width( ), im->height( ) / 2 );

		// The apps field 0 depends on the field order of the image
		if ( im->field_order( ) == top_field_first )
			field = field == 0 ? 0 : 1;
		else
			field = field == 1 ? 0 : 1;

		// Copy every second scan line from each plane to extract the field
		for ( size_t i = 0; i < im->plane_count( ); i ++ )
		{
			unsigned char *src = im->data( i ) + field * im->pitch( i );
			unsigned char *dst = new_im->data( i );
			int src_pitch = 2 * im->pitch( i );
			int dst_pitch = new_im->pitch( i );
			int linesize = new_im->linesize( i );
			int height = new_im->height( i );
			while( height -- )
			{
				memcpy( dst, src, linesize );
				dst += dst_pitch;
				src += src_pitch;
			}
		}
	}

	return new_im;
}

IL_DECLSPEC image_type_ptr deinterlace( const image_type_ptr &im )
{
	if ( im && im->field_order( ) != progressive )
	{
		im->set_field_order( progressive );
		for ( size_t i = 0; i < im->plane_count( ); i ++ )
		{
			unsigned char *dst = im->data( i );
			unsigned char *src = im->data( i ) + im->pitch( i );
			int linesize = im->linesize( i );
			int src_pitch = im->pitch( i ) - linesize;
			int height = im->height( i ) - 1;
			while( height -- )
			{
				for ( int w = 0; w < linesize; w ++ )
				{
					*dst = ( *src ++ + *dst ) >> 1;
					dst ++;
				}
				dst += src_pitch;
				src += src_pitch;
			}
		}
	}

	return im;
}

static image_type_ptr rgb_to_yuv444( const image_type_ptr &src_img, const std::wstring &format, int bytes, int r, int g, int b )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int y, u, v;

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		while( height -- )
		{
			width = orig_width;

			while( width -- )
			{
				rgb24_to_yuv444( y, u, v, *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				*dst ++ = ( unsigned char )y;
				*dst ++ = ( unsigned char )u;
				*dst ++ = ( unsigned char )v;
			}

			dst += dst_pitch;
			src += src_pitch;
		}
	}

	return dst_img;
}

static image_type_ptr yuv444_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		while( height -- )
		{
			while( width -- )
			{
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src, *( src + 1 ), *( src + 2 ) );
				src += 3;
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
			}

			dst += dst_pitch;
			src += src_pitch;
			width = orig_width;
		}
	}

	return dst_img;
}

static image_type_ptr rgb_to_yuv422( const image_type_ptr &src_img, const std::wstring &format, int bytes, int r, int g, int b )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int y[ 2 ], u[ 2 ], v[ 2 ];

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		while( height -- )
		{
			width = orig_width;

			while( width > 1 )
			{
				rgb24_to_yuv444( y[ 0 ], u[ 0 ], v[ 0 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 1 ], u[ 1 ], v[ 1 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				*dst ++ = ( unsigned char )y[ 0 ];
				*dst ++ = ( unsigned char )( ( u[ 0 ] + u[ 1 ] ) >> 1 );
				*dst ++ = ( unsigned char )y[ 1 ];
				*dst ++ = ( unsigned char )( ( v[ 0 ] + v[ 1 ] ) >> 1 );
				width -= 2;
			}

			dst += dst_pitch;
			src += src_pitch;
		}
	}

	return dst_img;
}

static image_type_ptr yuv422_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		while( height -- )
		{
			while( width > 1 )
			{
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src, *( src + 1 ), *( src + 3 ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				src += 2;
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src, *( src - 1 ), *( src + 1 ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				src += 2;
				width -= 2;
			}

			dst += dst_pitch;
			src += src_pitch;
			width = orig_width;
		}
	}

	return dst_img;
}

static image_type_ptr rgb_to_yuv422p( const image_type_ptr &src_img, const std::wstring &format, int bytes, int r, int g, int b )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int y[ 2 ], u[ 2 ], v[ 2 ];

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );

		pointer dst[ 3 ];
		dst[ 0 ] = dst_img->data( 0 );
		dst[ 1 ] = dst_img->data( 1 );
		dst[ 2 ] = dst_img->data( 2 );

		size_type dst_pitch[ 3 ];
		dst_pitch[ 0 ] = dst_img->pitch( 0 ) - dst_img->linesize( 0 );
		dst_pitch[ 1 ] = dst_img->pitch( 1 ) - dst_img->linesize( 1 );
		dst_pitch[ 2 ] = dst_img->pitch( 2 ) - dst_img->linesize( 2 );

		size_type orig_width = width;

		while( height > 1 )
		{
			width = orig_width;

			while( width > 1 )
			{
				rgb24_to_yuv444( y[ 0 ], u[ 0 ], v[ 0 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 1 ], u[ 1 ], v[ 1 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;

				*dst[ 0 ] ++ = ( unsigned char )y[ 0 ];
				*dst[ 0 ] ++ = ( unsigned char )y[ 1 ];

				*dst[ 1 ] ++ = ( unsigned char )( ( u[ 0 ] + u[ 1 ] ) >> 1 );
				*dst[ 2 ] ++ = ( unsigned char )( ( v[ 0 ] + v[ 1 ] ) >> 1 );

				width -= 2;
			}

			src += src_pitch;

			dst[ 0 ] += dst_pitch[ 0 ];
			dst[ 1 ] += dst_pitch[ 1 ];
			dst[ 2 ] += dst_pitch[ 2 ];

			height --;
		}
	}

	return dst_img;
}

static image_type_ptr yuv422p_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };

		const_pointer src[ 3 ];
		src[ 0 ] = src_img->data( 0 );
		src[ 1 ] = src_img->data( 1 );
		src[ 2 ] = src_img->data( 2 );

		size_type src_pitch[ 3 ];
		src_pitch[ 0 ] = src_img->pitch( 0 ) - src_img->linesize( 0 );
		src_pitch[ 1 ] = src_img->pitch( 1 ) - src_img->linesize( 1 );
		src_pitch[ 2 ] = src_img->pitch( 2 ) - src_img->linesize( 2 );

		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		pointer dst = dst_img->data( );

		size_type orig_width = width;

		while( height > 1 )
		{
			while( width > 1 )
			{
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *( src[ 0 ] ), *( src[ 1 ] ), *( src[ 2 ] ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				src[ 0 ] += 1;

				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *( src[ 0 ] ), *( src[ 1 ] ), *( src[ 2 ] ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				src[ 0 ] += 1;

				src[ 1 ] += 1;
				src[ 2 ] += 1;

				width -= 2;
			}

			src[ 0 ] += src_pitch[ 0 ];
			src[ 1 ] += src_pitch[ 1 ];
			src[ 2 ] += src_pitch[ 2 ];

			dst += dst_pitch;

			width = orig_width;
			height --;
		}
	}

	return dst_img;
}

static image_type_ptr rgb_to_yuv420p( const image_type_ptr &src_img, const std::wstring &format, int bytes, int r, int g, int b )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int y[ 4 ], u[ 4 ], v[ 4 ];

		const_pointer src[ 2 ];
		src[ 0 ] = src_img->data( );
		src[ 1 ] = src_img->data( ) + src_img->pitch( );
		size_type src_pitch = 2 * src_img->pitch( ) - src_img->linesize( );

		pointer dst[ 4 ];
		dst[ 0 ] = dst_img->data( 0 );
		dst[ 1 ] = dst_img->data( 0 ) + dst_img->pitch( 0 );
		dst[ 2 ] = dst_img->data( 1 );
		dst[ 3 ] = dst_img->data( 2 );

		size_type dst_pitch[ 3 ];
		dst_pitch[ 0 ] = 2 * dst_img->pitch( 0 ) - dst_img->linesize( 0 );
		dst_pitch[ 1 ] = dst_img->pitch( 1 ) - dst_img->linesize( 1 );
		dst_pitch[ 2 ] = dst_img->pitch( 2 ) - dst_img->linesize( 2 );

		size_type orig_width = width;

		while( height > 1 )
		{
			width = orig_width;

			while( width > 1 )
			{
				rgb24_to_yuv444( y[ 0 ], u[ 0 ], v[ 0 ], *( src[ 0 ] + r ), *( src[ 0 ] + g ), *( src[ 0 ] + b ) );
				src[ 0 ] += bytes;
				rgb24_to_yuv444( y[ 1 ], u[ 1 ], v[ 1 ], *( src[ 0 ] + r ), *( src[ 0 ] + g ), *( src[ 0 ] + b ) );
				src[ 0 ] += bytes;
				rgb24_to_yuv444( y[ 2 ], u[ 2 ], v[ 2 ], *( src[ 1 ] + r ), *( src[ 1 ] + g ), *( src[ 1 ] + b ) );
				src[ 1 ] += bytes;
				rgb24_to_yuv444( y[ 3 ], u[ 3 ], v[ 3 ], *( src[ 1 ] + r ), *( src[ 1 ] + g ), *( src[ 1 ] + b ) );
				src[ 1 ] += bytes;

				*dst[ 0 ] ++ = ( unsigned char )y[ 0 ];
				*dst[ 0 ] ++ = ( unsigned char )y[ 1 ];
				*dst[ 1 ] ++ = ( unsigned char )y[ 2 ];
				*dst[ 1 ] ++ = ( unsigned char )y[ 3 ];

				*dst[ 2 ] ++ = ( unsigned char )( ( u[ 0 ] + u[ 1 ] + u[ 2 ] + u[ 3 ] ) >> 2 );
				*dst[ 3 ] ++ = ( unsigned char )( ( v[ 0 ] + v[ 1 ] + v[ 2 ] + v[ 3 ] ) >> 2 );

				width -= 2;
			}

			src[ 0 ] += src_pitch;
			src[ 1 ] += src_pitch;

			dst[ 0 ] += dst_pitch[ 0 ];
			dst[ 1 ] += dst_pitch[ 0 ];
			dst[ 2 ] += dst_pitch[ 1 ];
			dst[ 3 ] += dst_pitch[ 2 ];

			height -= 2;
		}
	}

	return dst_img;
}

typedef unsigned char uchar;
#define assign3( dst, v1, v2, v3 )		*dst ++ = uchar( v1 ); *dst ++ = uchar( v2 ); *dst ++ = uchar( v3 );
#define assign4( dst, v1, v2, v3, v4 )	*dst ++ = uchar( v1 ); *dst ++ = uchar( v2 ); *dst ++ = uchar( v3 ); *dst ++ = uchar( v4 );

static image_type_ptr yuv420p_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };

		const_pointer src0 = src_img->data( 0 );
		const_pointer src1 = src_img->data( 0 ) + src_img->pitch( 0 );
		const_pointer src2 = src_img->data( 1 );
		const_pointer src3 = src_img->data( 2 );

		size_type src_pitch0 = 2 * src_img->pitch( 0 ) - src_img->linesize( 0 );
		size_type src_pitch1 = src_img->pitch( 1 ) - src_img->linesize( 1 );
		size_type src_pitch2 = src_img->pitch( 2 ) - src_img->linesize( 2 );
		size_type chroma_pitch = src_img->pitch( 1 );

		pointer dst0 = dst_img->data( );
		pointer dst1 = dst_img->data( ) + dst_img->pitch( );

		size_type dst_pitch = 2 * dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		unsigned char u, v;

		while( height > 1 )
		{
			if ( a == -1 )
			{
				while( width > 1 )
				{
					u = ( *src2 );
					v = ( *src3 );

					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2, *src3 );
					assign3( dst0, rgb[ r ], rgb[ g ], rgb[ b ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src1 ++, u, v );
					assign3( dst1, rgb[ r ], rgb[ g ], rgb[ b ] );

					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2 ++, *src3 ++ );
					assign3( dst0, rgb[ r ], rgb[ g ], rgb[ b ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src1 ++, u, v );
					assign3( dst1, rgb[ r ], rgb[ g ], rgb[ b ] );

					width -= 2;
				}

				if ( width )
				{
					u = ( *src2 + *( src2 + chroma_pitch ) ) >> 1;
					v = ( *src3 + *( src3 + chroma_pitch ) ) >> 1;
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2 ++, *src3 ++ );
					assign3( dst0, rgb[ r ], rgb[ g ], rgb[ b ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src1 ++, u, v );
					assign3( dst1, rgb[ r ], rgb[ g ], rgb[ b ] );
				}
			}
			else
			{
				while( width > 1 )
				{
					u = ( *src2 + *( src2 + chroma_pitch ) ) >> 1;
					v = ( *src3 + *( src3 + chroma_pitch ) ) >> 1;

					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2, *src3 );
					assign4( dst0, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src1 ++, u, v );
					assign4( dst1, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );

					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2 ++, *src3 ++ );
					assign4( dst0, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src1 ++, u, v );
					assign4( dst1, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );

					width -= 2;
				}

				if ( width )
				{
					u = ( *src2 + *( src2 + chroma_pitch ) ) >> 1;
					v = ( *src3 + *( src3 + chroma_pitch ) ) >> 1;
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2 ++, *src3 ++ );
					assign4( dst0, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src1 ++, u, v );
					assign4( dst1, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );
				}
			}

			src0 += src_pitch0;
			src1 += src_pitch0;
			src2 += src_pitch1;
			src3 += src_pitch2;

			dst0 += dst_pitch;
			dst1 += dst_pitch;

			width = orig_width;
			height -= 2;
		}

		if ( height )
		{
			if ( a != -1 )
			{
				while( width -- )
				{
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2, *src3 );
					assign3( dst0, rgb[ r ], rgb[ g ], rgb[ b ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2 ++, *src3 ++ );
					assign3( dst0, rgb[ r ], rgb[ g ], rgb[ b ] );
				}
			}
			else
			{
				while( width -- )
				{
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2, *src3 );
					assign4( dst0, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );
					yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src2 ++, *src3 ++ );
					assign4( dst0, rgb[ r ], rgb[ g ], rgb[ b ], rgb[ a ] );
				}
			}
		}
	}

	return dst_img;
}

static image_type_ptr rgb_to_yuv411( const image_type_ptr &src_img, const std::wstring &format, int bytes, int r, int g, int b )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int y[ 4 ], u[ 4 ], v[ 4 ];

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		while( height -- )
		{
			width = orig_width;

			while( width > 3 )
			{
				rgb24_to_yuv444( y[ 0 ], u[ 0 ], v[ 0 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 1 ], u[ 1 ], v[ 1 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 2 ], u[ 2 ], v[ 2 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 3 ], u[ 3 ], v[ 3 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				*dst ++ = ( unsigned char )y[ 0 ];
				*dst ++ = ( unsigned char )y[ 1 ];
				*dst ++ = ( unsigned char )( ( u[ 0 ] + u[ 1 ] + u[ 2 ] + u[ 3 ] ) >> 2 );
				*dst ++ = ( unsigned char )y[ 2 ];
				*dst ++ = ( unsigned char )y[ 3 ];
				*dst ++ = ( unsigned char )( ( v[ 0 ] + v[ 1 ] + v[ 2 ] + v[ 3 ] ) >> 2 );
				width -= 4;
			}

			dst += dst_pitch;
			src += src_pitch;
		}
	}

	return dst_img;
}

static image_type_ptr yuv411_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		size_type orig_width = width;

		while( height -- )
		{
			while( width > 3 )
			{
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src, *( src + 2 ), *( src + 5 ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *( src + 1 ), *( src + 2 ), *( src + 5 ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *( src + 3 ), *( src + 2 ), *( src + 5 ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *( src + 4 ), *( src + 2 ), *( src + 5 ) );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];
				src += 6;
				width -= 4;
			}

			dst += dst_pitch;
			src += src_pitch;
			width = orig_width;
		}
	}

	return dst_img;
}

static image_type_ptr rgb_to_yuv411p( const image_type_ptr &src_img, const std::wstring &format, int bytes, int r, int g, int b )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int y[ 4 ], u[ 4 ], v[ 4 ];

		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( 0 );

		pointer dst[ 3 ];
		dst[ 0 ] = dst_img->data( 0 );
		dst[ 1 ] = dst_img->data( 1 );
		dst[ 2 ] = dst_img->data( 2 );

		size_type dst_pitch[ 3 ];
		dst_pitch[ 0 ] = dst_img->pitch( 0 ) - dst_img->linesize( 0 );
		dst_pitch[ 1 ] = dst_img->pitch( 1 ) - dst_img->linesize( 1 );
		dst_pitch[ 2 ] = dst_img->pitch( 2 ) - dst_img->linesize( 2 );

		size_type orig_width = width;

		while( height )
		{
			width = orig_width;

			while( width > 3 )
			{
				rgb24_to_yuv444( y[ 0 ], u[ 0 ], v[ 0 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 1 ], u[ 1 ], v[ 1 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 2 ], u[ 2 ], v[ 2 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;
				rgb24_to_yuv444( y[ 3 ], u[ 3 ], v[ 3 ], *( src + r ), *( src + g ), *( src + b ) );
				src += bytes;

				*( dst[ 0 ] ++ ) = ( unsigned char )y[ 0 ];
				*( dst[ 0 ] ++ ) = ( unsigned char )y[ 1 ];
				*( dst[ 0 ] ++ ) = ( unsigned char )y[ 2 ];
				*( dst[ 0 ] ++ ) = ( unsigned char )y[ 3 ];

				*dst[ 1 ] ++ = ( unsigned char )( ( u[ 0 ] + u[ 1 ] + u[ 2 ] + u[ 3 ] ) >> 2 );
				*dst[ 2 ] ++ = ( unsigned char )( ( v[ 0 ] + v[ 1 ] + v[ 2 ] + v[ 3 ] ) >> 2 );

				width -= 4;
			}

			src += src_pitch;

			dst[ 0 ] += dst_pitch[ 0 ];
			dst[ 1 ] += dst_pitch[ 1 ];
			dst[ 2 ] += dst_pitch[ 2 ];

			height --;
		}
	}

	return dst_img;
}

static image_type_ptr yuv411p_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };

		register const_pointer src0 = src_img->data( 0 );
		register const_pointer src1 = src_img->data( 1 );
		register const_pointer src2 = src_img->data( 2 );

		register size_type src_pitch0 = src_img->pitch( 0 ) - src_img->linesize( 0 );
		register size_type src_pitch1 = src_img->pitch( 1 ) - src_img->linesize( 1 );
		register size_type src_pitch2 = src_img->pitch( 2 ) - src_img->linesize( 2 );

		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );

		register pointer dst = dst_img->data( );

		size_type orig_width = width;

		while( height -- )
		{
			while( width > 3 )
			{
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src1, *src2 );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];

				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src1, *src2 );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];

				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src1, *src2 );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];

				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src0 ++, *src1, *src2 );
				*dst ++ = ( unsigned char )rgb[ r ];
				*dst ++ = ( unsigned char )rgb[ g ];
				*dst ++ = ( unsigned char )rgb[ b ];
				if ( a != -1 ) *dst ++ = ( unsigned char )rgb[ a ];

				src1 += 1;
				src2 += 1;

				width -= 4;
			}

			src0 += src_pitch0;
			src1 += src_pitch1;
			src2 += src_pitch2;

			dst += dst_pitch;

			width = orig_width;
		}
	}

	return dst_img;
}

static image_type_ptr l8a8_to_rgb( const image_type_ptr &src_img, const std::wstring &format, int r, int g, int b, int a )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		int rgb[ 4 ] = { 255, 255, 255, 255 };
		
		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( ) - src_img->linesize( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( ) - dst_img->linesize( );
		
		for( int i = 0; i < height; ++i )
		{
			for( int j = 0; j < width; ++j )
			{
				yuv444_to_rgb24( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ], *src++, 0, 0 );
				*dst++ = ( unsigned char ) rgb[ r ];
				*dst++ = ( unsigned char ) rgb[ g ];
				*dst++ = ( unsigned char ) rgb[ b ];
				if( a != -1 ) *dst++ = *src++;
			}
			
			src += src_pitch;
			dst += dst_pitch;
		}
	}
}

struct rgb_map
{
	std::wstring from;
	std::wstring to;
	int bytes_in;
	int in[ 4 ];
	int bytes_out;
	int out[ 4 ];
};

struct rgb_map rgb_mapping[ ] =
{
	{ L"r8g8b8", L"b8g8r8", 3, { 0, 1, 2, 0 }, 3, { 2, 1, 0, 0 } },
	{ L"r8g8b8", L"r8g8b8a8", 3, { 0, 1, 2, 0 }, 4, { 0, 1, 2, 3 } },
	{ L"r8g8b8", L"b8g8r8a8", 3, { 0, 1, 2, 0 }, 4, { 2, 1, 0, 3 } },
	{ L"r8g8b8", L"a8b8g8r8", 3, { 0, 1, 2, 0 }, 4, { 3, 2, 1, 0 } },
	{ L"b8g8r8", L"r8g8b8", 3, { 2, 1, 0, 0 }, 3, { 0, 1, 2, 0 } },
	{ L"b8g8r8", L"r8g8b8a8", 3, { 2, 1, 0, 0 }, 4, { 0, 1, 2, 3 } },
	{ L"b8g8r8", L"a8b8g8r8", 3, { 2, 1, 0, 0 }, 4, { 3, 2, 1, 0 } },
	{ L"b8g8r8", L"b8g8r8a8", 3, { 2, 1, 0, 0 }, 4, { 2, 1, 0, 3 } },
	{ L"r8g8b8a8", L"r8g8b8", 4, { 0, 1, 2, 0 }, 3, { 0, 1, 2, 0 } },
	{ L"r8g8b8a8", L"b8g8r8", 4, { 0, 1, 2, 0 }, 3, { 2, 1, 0, 0 } },
	{ L"r8g8b8a8", L"b8g8r8a8", 4, { 0, 1, 2, 0 }, 4, { 2, 1, 0, 3 } },
	{ L"b8g8r8a8", L"r8g8b8", 4, { 2, 1, 0, 0 }, 3, { 0, 1, 2, 0 } },
	{ L"b8g8r8a8", L"b8g8r8", 4, { 2, 1, 0, 0 }, 3, { 2, 1, 0, 0 } },
	{ L"b8g8r8a8", L"r8g8b8a8", 4, { 2, 1, 0, 3 }, 4, { 0, 1, 2, 3 } },
	{ L"b8g8r8a8", L"a8b8g8r8", 4, { 2, 1, 0, 3 }, 4, { 3, 2, 1, 0 } },
	{ L"a8r8g8b8", L"a8b8g8r8", 4, { 1, 2, 3, 0 }, 4, { 3, 2, 1, 0 } },
	{ L"a8b8g8r8", L"b8g8r8a8", 4, { 3, 2, 1, 0 }, 4, { 2, 1, 0, 3 } },
	{ L"a8b8g8r8", L"r8g8b8a8", 4, { 3, 2, 1, 0 }, 4, { 0, 1, 2, 3 } },
	{ L"l8a8", L"b8g8r8a8", 2, { 0, 0, 0, 1 }, 4, { 2, 1, 0, 3 } },
	{ L"l8a8", L"b8g8r8", 2, { 0, 0, 0, 1 }, 3, { 2, 1, 0, 0 } },
	{ L"", L"", 0, { 0, 0, 0, 0 }, 0, { 0, 0, 0, 0 } }
};

static image_type_ptr rgb_to_rgb( const image_type_ptr &src_img, const std::wstring &format )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		const_pointer src = src_img->data( );
		size_type src_pitch = src_img->pitch( );
		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( );

		const_pointer sptr = src;
		pointer dptr = dst;

		size_type orig_width = width;

		int i = 0;
		while ( rgb_mapping[ i ].from != L"" )
		{
			if ( rgb_mapping[ i ].from == src_img->pf( ) && rgb_mapping[ i ].to == format )
				break;
			i ++;
		}

		rgb_map &map = rgb_mapping[ i ];
		size_type oc = map.bytes_out;
		size_type ic = map.bytes_in;

		bool has_alpha = ic == 4 || src_img->pf( ) == L"l8a8";

		while( height -- )
		{
			while( width -- )
			{
				switch( oc )
				{
					case 4:		*( dst + map.out[ 3 ] ) = has_alpha  ? *( src + map.in[ 3 ] ) : 255;
					case 3:		*( dst + map.out[ 2 ] ) = *( src + map.in[ 2 ] );
					case 2:		*( dst + map.out[ 1 ] ) = *( src + map.in[ 1 ] );
					case 1:		*( dst + map.out[ 0 ] ) = *( src + map.in[ 0 ] );
				};

				src += ic;
				dst += oc;
			}

			dst = dptr += dst_pitch;
			src = sptr += src_pitch;
			width = orig_width;
		}
	}

	return dst_img;
}

static image_type_ptr yuv420p_to_yuv422( const image_type_ptr &src_img, const std::wstring &format )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		const_pointer src[ 3 ];
		src[ 0 ] = src_img->data( 0 );
		src[ 1 ] = src_img->data( 1 );
		src[ 2 ] = src_img->data( 2 );

		size_type src_pitch[ 3 ]; 
		src_pitch[ 0 ] = src_img->pitch( 0 );
		src_pitch[ 1 ] = src_img->pitch( 1 );
		src_pitch[ 2 ] = src_img->pitch( 2 );

		pointer dst = dst_img->data( );
		size_type dst_pitch = dst_img->pitch( );

		const_pointer sptr[ 3 ]; 
		sptr[ 0 ] = src[ 0 ];
		sptr[ 1 ] = src[ 1 ];
		sptr[ 2 ] = src[ 2 ];

		pointer dptr = dst;

		size_type orig_width = width / 2;

		while( height -- )
		{
			width = orig_width;
			dst = dptr;
			src[ 0 ] = sptr[ 0 ];
			src[ 1 ] = sptr[ 1 ];
			src[ 2 ] = sptr[ 2 ];

			while( width > 1 )
			{
				*dst ++ = *( src[ 0 ] ++ );
				*dst ++ = *( src[ 1 ] ++ );
				*dst ++ = *( src[ 0 ] ++ );
				*dst ++ = *( src[ 2 ] ++ );

				*dst ++ = *( src[ 0 ] ++ );
				*dst ++ = *( src[ 1 ] ++ );
				*dst ++ = *( src[ 0 ] ++ );
				*dst ++ = *( src[ 2 ] ++ );

				width -= 2;
			}

			dptr += dst_pitch;
			sptr[ 0 ] += src_pitch[ 0 ];
			if ( height % 2 )
			{
				sptr[ 1 ] += src_pitch[ 1 ];
				sptr[ 2 ] += src_pitch[ 2 ];
			}
		}
	}

	return dst_img;
}

static image_type_ptr yuv420p_to_yuv444( const image_type_ptr &src_img, const std::wstring &format )
{
	size_type width = src_img->width( );
	size_type height = src_img->height( );

	image_type_ptr dst_img = allocate( src_img, format );

	if ( dst_img != 0 )
	{
		const_pointer src[ 4 ];
		src[ 0 ] = src_img->data( 0 );
		src[ 1 ] = src_img->data( 1 );
		src[ 2 ] = src_img->data( 2 );
		src[ 3 ] = src[ 0 ] + src_img->pitch( 0 );

		size_type src_pitch[ 3 ]; 
		src_pitch[ 0 ] = src_img->pitch( 0 ) * 2;
		src_pitch[ 1 ] = src_img->pitch( 1 );
		src_pitch[ 2 ] = src_img->pitch( 2 );

		pointer dst[ 2 ];
		size_type dst_pitch = dst_img->pitch( ) * 2;
		dst[ 0 ] = dst_img->data( );
		dst[ 1 ] = dst_img->data( ) + width * 3;

		const_pointer sptr[ 4 ]; 
		sptr[ 0 ] = src[ 0 ];
		sptr[ 1 ] = src[ 1 ];
		sptr[ 2 ] = src[ 2 ];
		sptr[ 3 ] = src[ 3 ];

		pointer dptr[ 2 ];
		dptr[ 0 ] = dst[ 0 ];
		dptr[ 1 ] = dst[ 1 ];

		size_type orig_width = width;

		while( height > 1 )
		{
			width = orig_width;
			dst[ 0 ] = dptr[ 0 ];
			dst[ 1 ] = dptr[ 1 ];
			src[ 0 ] = sptr[ 0 ];
			src[ 1 ] = sptr[ 1 ];
			src[ 2 ] = sptr[ 2 ];
			src[ 3 ] = sptr[ 3 ];

			while( width > 1 )
			{
				*( dst[ 0 ] ++ ) = *( src[ 0 ] ++ );
				*( dst[ 0 ] ++ ) = *( src[ 1 ] );
				*( dst[ 0 ] ++ ) = *( src[ 2 ] );

				*( dst[ 0 ] ++ ) = *( src[ 0 ] ++ );
				*( dst[ 0 ] ++ ) = *( src[ 1 ] );
				*( dst[ 0 ] ++ ) = *( src[ 2 ] );

				*( dst[ 1 ] ++ ) = *( src[ 3 ] ++ );
				*( dst[ 1 ] ++ ) = *( src[ 1 ] );
				*( dst[ 1 ] ++ ) = *( src[ 2 ] );

				*( dst[ 1 ] ++ ) = *( src[ 3 ] ++ );
				*( dst[ 1 ] ++ ) = *( src[ 1 ] ++ );
				*( dst[ 1 ] ++ ) = *( src[ 2 ] ++ );

				width -= 2;
			}

			dptr[ 0 ] += dst_pitch;
			dptr[ 1 ] += dst_pitch;
			sptr[ 0 ] += src_pitch[ 0 ];
			sptr[ 1 ] += src_pitch[ 1 ];
			sptr[ 2 ] += src_pitch[ 2 ];
			sptr[ 3 ] += src_pitch[ 0 ];

			height -= 2;
		}
	}

	return dst_img;
}

IL_DECLSPEC image_type_ptr convert( const image_type_ptr &src, const std::wstring &dst_pf, int )
{
	// Sanity check
	if ( src == 0 )
		return src;

	// Get the format of the source
	std::wstring src_pf = src->pf( );

	// Convert
	if ( dst_pf == src_pf )
	{
		return src;
	}
	else if ( src_pf == L"r8g8b8" )
	{
		if ( dst_pf == L"b8g8r8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"r8g8b8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"b8g8r8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"yuv444" )
			return rgb_to_yuv444( src, dst_pf, 3, 0, 1, 2 );
		else if ( dst_pf == L"yuv422" )
			return rgb_to_yuv422( src, dst_pf, 3, 0, 1, 2 );
		else if ( dst_pf == L"yuv422p" )
			return rgb_to_yuv422p( src, dst_pf, 3, 0, 1, 2 );
		else if ( dst_pf == L"yuv420p" )
			return rgb_to_yuv420p( src, dst_pf, 3, 0, 1, 2 );
		else if ( dst_pf == L"yuv411" )
			return rgb_to_yuv411( src, dst_pf, 3, 0, 1, 2 );
		else if ( dst_pf == L"yuv411p" )
			return rgb_to_yuv411p( src, dst_pf, 3, 0, 1, 2 );
	}
	else if ( src_pf == L"b8g8r8" )
	{
		if ( dst_pf == L"r8g8b8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"r8g8b8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"b8g8r8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"yuv444" )
			return rgb_to_yuv444( src, dst_pf, 3, 2, 1, 0 );
		else if ( dst_pf == L"yuv422" )
			return rgb_to_yuv422( src, dst_pf, 3, 2, 1, 0 );
		else if ( dst_pf == L"yuv422p" )
			return rgb_to_yuv422p( src, dst_pf, 3, 2, 1, 0 );
		else if ( dst_pf == L"yuv420p" )
			return rgb_to_yuv420p( src, dst_pf, 3, 2, 1, 0 );
		else if ( dst_pf == L"yuv411" )
			return rgb_to_yuv411( src, dst_pf, 3, 2, 1, 0 );
		else if ( dst_pf == L"yuv411p" )
			return rgb_to_yuv411p( src, dst_pf, 3, 2, 1, 0 );
	}
	else if ( src_pf == L"r8g8b8a8" )
	{
		if ( dst_pf == L"r8g8b8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"b8g8r8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"b8g8r8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"yuv444" )
			return rgb_to_yuv444( src, dst_pf, 4, 0, 1, 2 );
		else if ( dst_pf == L"yuv422" )
			return rgb_to_yuv422( src, dst_pf, 4, 0, 1, 2 );
		else if ( dst_pf == L"yuv422p" )
			return rgb_to_yuv422p( src, dst_pf, 4, 0, 1, 2 );
		else if ( dst_pf == L"yuv420p" )
			return rgb_to_yuv420p( src, dst_pf, 4, 0, 1, 2 );
		else if ( dst_pf == L"yuv411" )
			return rgb_to_yuv411( src, dst_pf, 4, 0, 1, 2 );
		else if ( dst_pf == L"yuv411p" )
			return rgb_to_yuv411p( src, dst_pf, 4, 0, 1, 2 );
	}
	else if ( src_pf == L"b8g8r8a8" )
	{
		if ( dst_pf == L"r8g8b8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"r8g8b8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"b8g8r8" )
			return rgb_to_rgb( src, dst_pf );
		else if ( dst_pf == L"yuv444" )
			return rgb_to_yuv444( src, dst_pf, 4, 2, 1, 0 );
		else if ( dst_pf == L"yuv422" )
			return rgb_to_yuv422( src, dst_pf, 4, 2, 1, 0 );
		else if ( dst_pf == L"yuv422p" )
			return rgb_to_yuv422p( src, dst_pf, 4, 2, 1, 0 );
		else if ( dst_pf == L"yuv420p" )
			return rgb_to_yuv420p( src, dst_pf, 4, 2, 1, 0 );
		else if ( dst_pf == L"yuv411" )
			return rgb_to_yuv411( src, dst_pf, 4, 2, 1, 0 );
		else if ( dst_pf == L"yuv411p" )
			return rgb_to_yuv411p( src, dst_pf, 4, 2, 1, 0 );
	}
	else if ( src_pf == L"a8r8g8b8" )
	{
		if( dst_pf == L"a8b8g8r8" )
			return rgb_to_rgb( src, dst_pf );
	}
	else if( src_pf == L"a8b8g8r8" )
	{
		if( dst_pf == L"b8g8r8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if( dst_pf == L"r8g8b8a8" )
			return rgb_to_rgb( src, dst_pf );
	}
	else if ( src_pf == L"yuv444" )
	{
		if ( dst_pf == L"r8g8b8" )
			return yuv444_to_rgb( src, dst_pf, 0, 1, 2, -1 );
		else if ( dst_pf == L"b8g8r8" )
			return yuv444_to_rgb( src, dst_pf, 2, 1, 0, -1 );
		else if ( dst_pf == L"r8g8b8a8" )
			return yuv444_to_rgb( src, dst_pf, 0, 1, 2, 3 );
		else if ( dst_pf == L"b8g8r8a8" )
			return yuv444_to_rgb( src, dst_pf, 2, 1, 0, 3 );
	}
	else if ( src_pf == L"yuv422" )
	{
		if ( dst_pf == L"r8g8b8" )
			return yuv422_to_rgb( src, dst_pf, 0, 1, 2, -1 );
		else if ( dst_pf == L"b8g8r8" )
			return yuv422_to_rgb( src, dst_pf, 2, 1, 0, -1 );
		else if ( dst_pf == L"r8g8b8a8" )
			return yuv422_to_rgb( src, dst_pf, 0, 1, 2, 3 );
		else if ( dst_pf == L"b8g8r8a8" )
			return yuv422_to_rgb( src, dst_pf, 2, 1, 0, 3 );
	}
	else if ( src_pf == L"yuv422p" )
	{
		if ( dst_pf == L"r8g8b8" )
			return yuv422p_to_rgb( src, dst_pf, 0, 1, 2, -1 );
		else if ( dst_pf == L"b8g8r8" )
			return yuv422p_to_rgb( src, dst_pf, 2, 1, 0, -1 );
		else if ( dst_pf == L"r8g8b8a8" )
			return yuv422p_to_rgb( src, dst_pf, 0, 1, 2, 3 );
		else if ( dst_pf == L"b8g8r8a8" )
			return yuv422p_to_rgb( src, dst_pf, 2, 1, 0, 3 );
	}
	else if ( src_pf == L"yuv420p" )
	{
		if ( dst_pf == L"r8g8b8" )
			return yuv420p_to_rgb( src, dst_pf, 0, 1, 2, -1 );
		else if ( dst_pf == L"b8g8r8" )
			return yuv420p_to_rgb( src, dst_pf, 2, 1, 0, -1 );
		else if ( dst_pf == L"r8g8b8a8" )
			return yuv420p_to_rgb( src, dst_pf, 0, 1, 2, 3 );
		else if ( dst_pf == L"b8g8r8a8" )
			return yuv420p_to_rgb( src, dst_pf, 2, 1, 0, 3 );
		else if ( dst_pf == L"yuv422" )
			return yuv420p_to_yuv422( src, dst_pf );
		else if ( dst_pf == L"yuv444" )
			return yuv420p_to_yuv444( src, dst_pf );
	}
	else if ( src_pf == L"yuv411" )
	{
		if ( dst_pf == L"r8g8b8" )
			return yuv411_to_rgb( src, dst_pf, 0, 1, 2, -1 );
		else if ( dst_pf == L"b8g8r8" )
			return yuv411_to_rgb( src, dst_pf, 2, 1, 0, -1 );
		else if ( dst_pf == L"r8g8b8a8" )
			return yuv411_to_rgb( src, dst_pf, 0, 1, 2, 3 );
		else if ( dst_pf == L"b8g8r8a8" )
			return yuv411_to_rgb( src, dst_pf, 2, 1, 0, 3 );
	}
	else if ( src_pf == L"yuv411p" )
	{
		if ( dst_pf == L"r8g8b8" )
			return yuv411p_to_rgb( src, dst_pf, 0, 1, 2, -1 );
		else if ( dst_pf == L"b8g8r8" )
			return yuv411p_to_rgb( src, dst_pf, 2, 1, 0, -1 );
		else if ( dst_pf == L"r8g8b8a8" )
			return yuv411p_to_rgb( src, dst_pf, 0, 1, 2, 3 );
		else if ( dst_pf == L"b8g8r8a8" )
			return yuv411p_to_rgb( src, dst_pf, 2, 1, 0, 3 );
	}
	else if( src_pf == L"l8a8" )
	{
		if( dst_pf == L"b8g8r8a8" )
			return rgb_to_rgb( src, dst_pf );
		else if( dst_pf == L"b8g8r8" )
			return rgb_to_rgb( src, dst_pf );
		else if( dst_pf == L"r8g8b8a8" )
			return rgb_to_rgb( src, dst_pf );
	}

#ifndef WIN32
	fprintf( stderr, "Unknown %s to %s\n", opl::to_string( src_pf ).c_str( ), opl::to_string( dst_pf ).c_str( ) );
#endif

	return image_type_ptr( );
}

image_type_ptr rescale_image( const image_type_ptr& im, int new_w, int new_h, int new_d, int bs, rescale_filter filter )
{
	image_type_ptr new_im = allocate( im->pf( ), new_w, new_h );
	if( !new_im )
		return im;

	int src_w = im->width( );
	int src_h = im->height( );
	int src_d = im->depth( );

	image_type::const_pointer	src = im->data( );
	image_type::pointer			dst = new_im->data( );

	if( filter == POINT_SAMPLING )
	{
		for( int k = 0; k < new_d; ++k )
		{
			int dk = k * ( src_d - 1 ) / new_d;
			for( int j = 0; j < new_h; ++j )
			{
				int dj = j * ( src_h - 1 ) / new_h;
				for( int i = 0; i < new_w; ++i )
				{
					int di = i * ( src_w - 1 ) / new_w;
					for( int c = 0; c < bs; ++c )
						*dst++ = src[ ( ( dk * src_h + dj ) * src_w + di ) * bs + c ];
				}
				
				dst += new_im->pitch( ) - new_im->linesize( );
			}
		}
	}
	else if( filter == BILINEAR_SAMPLING )
	{
		float ddi = float( src_w - 1 ) / max( new_w - 1, 1 );
		float ddj = float( src_h - 1 ) / max( new_h - 1, 1 );
		float ddk = float( src_d - 1 ) / max( new_d - 1, 1 );

		for( int k = 0; k < new_d; ++k )
		{
			float sk = k * ddk;
			int dk = ( int ) sk;
			sk -= dk;
			int dk0 = min( dk + 1, src_d - 1 );
			for( int j = 0; j < new_h; ++j )
			{
				float sj = j * ddj;
				int dj = ( int ) sj;
				sj -= dj;
				int dj0 = min( dj + 1, src_h - 1 );
				for( int i = 0; i < new_w; ++i )
				{
					float si = i * ddi;
					int di = ( int ) si;
					si -= di;
					int di0 = min( di + 1, src_w - 1 );
					for( int c = 0; c < bs; ++c )
					{
						*dst++ = ( unsigned char ) (
							( (   src[ ( ( dk  * src_h + dj  ) * src_w + di  ) * bs + c ] * ( 1.0f - si ) +
								  src[ ( ( dk  * src_h + dj  ) * src_w + di0 ) * bs + c ] * (        si ) ) * ( 1.0f - sj ) +
								( src[ ( ( dk  * src_h + dj0 ) * src_w + di  ) * bs + c ] * ( 1.0f - si ) +
								  src[ ( ( dk  * src_h + dj0 ) * src_w + di0 ) * bs + c ] * (        si ) ) *          sj ) * ( 1.0f - sk ) +
							( (   src[ ( ( dk0 * src_h + dj  ) * src_w + di  ) * bs + c ] * ( 1.0f - si ) +
								  src[ ( ( dk0 * src_h + dj  ) * src_w + di0 ) * bs + c ] * (        si ) ) * ( 1.0f - sj ) +
								( src[ ( ( dk0 * src_h + dj0 ) * src_w + di  ) * bs + c ] * ( 1.0f - si ) +
								  src[ ( ( dk0 * src_h + dj0 ) * src_w + di0 ) * bs + c ] * (        si ) ) *          sj ) *          sk );
					}					
				}

				dst += new_im->pitch( ) - new_im->linesize( );
			}
		}
	}
	
	return new_im;
}

image_type_ptr rescale( const image_type_ptr &im, int new_w, int new_h, int new_d, rescale_filter filter )
{
	if( im->width( ) == new_w && im->height( ) == new_h && im->depth( ) == new_d )
		return im;
	
	// Goncalo - we need some enums for the pixel formats really quick!
	if( ( im->pf( ) == L"r8g8b8" ) || ( im->pf( ) == L"b8g8r8" ) || ( im->pf( ) == L"yuv444" ) )
		return rescale_image( im, new_w, new_h, new_d, 3, filter );
	else if( ( im->pf( ) == L"r8g8b8a8" ) || ( im->pf( ) == L"b8g8r8a8" ) )
		return rescale_image( im, new_w, new_h, new_d, 4, filter );
	
	return im;
}

IL_DECLSPEC void histogram( const image_type_ptr& im, int size, float mask[ ], histogram_type& hist )
{	
	int w = im->width( );
	int h = im->height( );
	int d = im->depth( );

	// luminance or channels - luminance as a name is just a simplification.
	std::vector<float> luminance( w * h * d, 0.0f );
	typedef std::vector<float>::pointer pointer;

	hist.clear( );
	hist.resize( size, 0 );

	const float one_over_255 = 1.0f / 256.0f;

	image_type_ptr new_im = convert( im, L"r8g8b8a8" );
	image_type::const_pointer src = new_im->data( );

	for( int k = 0; k < d; ++k )
	{
		for( int j = 0; j < h; ++j )
		{
			for( int i = 0; i < w; ++i )
			{
				int partial_idx = ( k * h + j  ) * w + i;
				luminance[ partial_idx ] =	src[ partial_idx * 4 + 0 ] * mask[ 0 ] * one_over_255
										+	src[ partial_idx * 4 + 1 ] * mask[ 1 ] * one_over_255
										+	src[ partial_idx * 4 + 2 ] * mask[ 2 ] * one_over_255
										+	src[ partial_idx * 4 + 3 ] * mask[ 3 ] * one_over_255;
				
				if( luminance[ partial_idx ] < 0.0f )
					luminance[ partial_idx ] = 0.0f;
				else if( luminance[ partial_idx ] > 1.0f )
					luminance[ partial_idx ] = 1.0f;
						
				hist[ static_cast<unsigned int>( size * luminance[ partial_idx ] ) ]++;
			}
		}
	}
}

IL_DECLSPEC void histogram( const image_type_ptr& im, int size, histogram_filter filter, histogram_type& hist )
{
	float mask[ 4 ];

	if( filter == LUMINANCE )
	{
		mask[ 0 ] = 0.299f;
		mask[ 1 ] = 0.587f;
		mask[ 2 ] = 0.114f;
		mask[ 3 ] = 0.0f;
	}
	else if( filter == RED )
	{
		mask[ 0 ] = 1.0f;
		mask[ 1 ] = 0.0f;
		mask[ 2 ] = 0.0f;
		mask[ 3 ] = 0.0f;
	}
	else if( filter == GREEN )
	{
		mask[ 0 ] = 0.0f;
		mask[ 1 ] = 1.0f;
		mask[ 2 ] = 0.0f;
		mask[ 3 ] = 0.0f;
	}
	else if( filter == BLUE )
	{
		mask[ 0 ] = 0.0f;
		mask[ 1 ] = 0.0f;
		mask[ 2 ] = 1.0f;
		mask[ 3 ] = 0.0f;
	}
	else if( filter == ALPHA )
	{
		mask[ 0 ] = 0.0f;
		mask[ 1 ] = 0.0f;
		mask[ 2 ] = 0.0f;
		mask[ 3 ] = 1.0f;
	}
	
	histogram( im, size, mask, hist );
}

} } }
